#ifndef SWFTag_h__
#define SWFTag_h__

#include "SWFTypes.h"
#include "SWFStructs.h"
#include <string>
#include <vector>
#include "ByteArray.h"
#include "SWFInputStream.h"
#include "SWFOutputStream.h"

class TagHandler;
class DefineTag;

#define HAS_CLIP_ACTION  1 << 7
#define HAS_CLIP_DEPTH  1 << 6
#define HAS_NAME  1 << 5
#define HAS_RATIO  1 << 4
#define HAS_CXFORM  1 << 3
#define HAS_MATRIX  1 << 2
#define HAS_CHARACTER  1 << 1
#define HAS_MOVE  1 << 0

#define HAS_IMAGE  1 << 4
#define HAS_CLASS_NAME  1 << 3
#define HAS_CACHE_AS_BITMAP  1 << 2
#define HAS_BLEND_MODE  1 << 1
#define HAS_FILTER_LIST  1 << 0

class SWFTag
{
public:
	int code;
	size_t length;
	char* data;
protected:
	std::string _tagName;
	std::vector<SWFTag*> _references;
public:
	SWFTag();
	~SWFTag();
	void putReference(SWFTag* tag);
	virtual void setData(char* data, size_t len);
	virtual void read(SWFInputStream* stream);
	virtual void getReferences(std::vector<SWFTag*>&);
	virtual void write(SWFOutputStream* stream);
	virtual void visit(TagHandler*);
	virtual std::string getTagName() const;
};

class DefineTag
	: public SWFTag
{
public:
	UI16 characterID;
};

#pragma region Control Tags

class TagMetadata : public SWFTag
{
public:
	std::string text;
public:
	void setData(char* data, size_t len) override;
	void visit(TagHandler*) override;
};

class TagProductInfo
	: public SWFTag
{
public:
	void visit(TagHandler* h);
};

class TagSetBackgroundColor
	: public SWFTag
{
public:
	ColorRGB color;
public:
	TagSetBackgroundColor();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagFrameLabel
	: public SWFTag
{
public:
	std::string name;
public:
	TagFrameLabel();
	void visit(TagHandler* h) override;
};

class TagFileAttributes
	: public SWFTag
{
public:
	bool UseDirectBlit;
	bool UseGPU;
	bool HasMetadata;
	bool ActionScript3;
	bool UseNetwork;

public:
	TagFileAttributes();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagEnd
	: public SWFTag
{
public:
	TagEnd();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagScriptLimits
	: public SWFTag
{
public:
	TagScriptLimits();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagSymbolClass
	: public SWFTag
{
public:
	TagSymbolClass();
	void visit(TagHandler* h) override;
};

class TagImportAssets2
	: public SWFTag
{
public:
	TagImportAssets2();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagProtect
	: public SWFTag
{
public:
	TagProtect();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagExportAssets
	: public SWFTag
{
public:
	TagExportAssets();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagEnableDebugger
	: public SWFTag
{
public:
	TagEnableDebugger();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagEnableDebugger2
	: public SWFTag
{
public:
	TagEnableDebugger2();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagSetTabIndex
	: public SWFTag
{
public:
	TagSetTabIndex();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineScalingGrid
	: public DefineTag
{
public:
	Rect Splitter;
public:
	TagDefineScalingGrid();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineSceneAndFrameLabelData
	: public DefineTag
{
public:
	TagDefineSceneAndFrameLabelData();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};
#pragma endregion Control Tags

#pragma region Shapes
class TagDefineShape
	: public DefineTag
{
public:
	Rect ShapeBounds;
	ShapeWithStyle* Shapes;
public:
	TagDefineShape();
	void visit(TagHandler* h) override;
};

class TagDefineShape2
	: public TagDefineShape
{
public:
	TagDefineShape2();
	void visit(TagHandler* h) override;
};

class TagDefineShape3
	: public TagDefineShape
{
public:
	TagDefineShape3();
	void visit(TagHandler* h) override;
};

class TagDefineShape4
	: public TagDefineShape
{
public:
	Rect EdgeBounds;
	// UB[1]  If 1, use fill winding rule. Minimum
	// file format version is SWF 10
	UB UsesFillWindingRule;
	// UB[1]  If 1, the shape contains at least one
	// non-scaling stroke.
	UB UsesNonScalingStrokes;
	// UB[1]  If 1, the shape contains at least one
	// scaling stroke.
	UB UsesScalingStrokes;
public:
	TagDefineShape4();
	void visit(TagHandler* h) override;
};
#pragma endregion Shapes

#pragma region Buttons

class TagDefineButton
	: public DefineTag
{
public:
	TagDefineButton();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len) override;
};

class TagDefineButton2
	: public DefineTag
{
public:
	TagDefineButton2();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len) override;
};

class TagDefineButtonCxform
	: public DefineTag
{
public:
	TagDefineButtonCxform();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len) override;
};

class TagDefineButtonSound
	: public DefineTag
{
public:
	TagDefineButtonSound();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len) override;
};

#pragma endregion Buttons

#pragma region Sprites And Movie Clips
class TagDefineSprite
	: public DefineTag
{
public:
	UI16 frameCount;
	std::vector<SWFTag*> tags;
public:
	TagDefineSprite();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};
#pragma endregion Sprites And Movie Clips

#pragma region Shape Morphing
class TagDefineMorphShape
	: public DefineTag
{
public:
	TagDefineMorphShape();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineMorphShape2
	: public DefineTag
{
public:
	TagDefineMorphShape2();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};
#pragma endregion Shape Morphing

#pragma region Fonts and Text
class TagDefineFont
	: public DefineTag
{
public:
	TagDefineFont();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineFont2
	: public DefineTag
{
public:
	TagDefineFont2();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineFont3
	: public DefineTag
{
public:
	TagDefineFont3();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineFont4
	: public DefineTag
{
public:
	TagDefineFont4();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineFontInfo
	: public DefineTag
{
public:
	TagDefineFontInfo();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineFontInfo2
	: public DefineTag
{
public:
	TagDefineFontInfo2();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineFontAlignZones
	: public DefineTag
{
public:
	TagDefineFontAlignZones();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineFontName
	: public DefineTag
{
public:
	TagDefineFontName();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineText
	: public DefineTag
{
public:
	TagDefineText();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineText2
	: public DefineTag
{
public:
	TagDefineText2();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagDefineEditText
	: public DefineTag
{
public:
	TagDefineEditText();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagCSMTextSettings
	: public SWFTag
{
public:
	TagCSMTextSettings();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};
#pragma endregion Fonts and Text

#pragma region The Display List
class TagShowFrame
	: public SWFTag
{
public:
	TagShowFrame();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagPlaceObject
	: public SWFTag
{
public:
	UI16 Depth;
	Matrix Matrix;
	CXFORM ColorTransform;
public:
	TagPlaceObject();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
};

class TagPlaceObject2
	: public TagPlaceObject
{
public:
	UI8 flags1;

	DefineTag* Character;
	UI16 Ratio;
	std::string Name;
	UI16 ClipDepth;
public:
	TagPlaceObject2();
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len);
public:
	inline bool hasClipAction()
	{
		return (flags1 & HAS_CLIP_ACTION) != 0;
	}

	inline bool hasClipDepth()
	{
		return (flags1 & HAS_CLIP_DEPTH) != 0;
	}

	inline bool hasMatrix()
	{
		return (flags1 & HAS_MATRIX) != 0;
	}

	inline bool hasCxform()
	{
		return (flags1 & HAS_CXFORM) != 0;
	}

	inline bool hasName()
	{
		return (flags1 & HAS_NAME) != 0;
	}

	inline bool hasRatio()
	{
		return (flags1 & HAS_RATIO) != 0;
	}

	inline bool hasMove()
	{
		return (flags1 & HAS_MOVE) != 0;
	}

	inline bool hasCharID()
	{
		return (flags1 & HAS_CHARACTER) != 0;
	}
};

class TagPlaceObject3
	: public TagPlaceObject2
{
public:
	UI8 flags2;

	std::string ClassName;
	std::vector<Filter> SurfaceFilterList;
	UI8 BlendMode;
	UI8 BitmapCache;
	UI8 Visible;
	ColorRGBA BackgroundColor;
public:
	TagPlaceObject3();
public:
	inline bool hasBlendMode()
	{
		return (flags2 & HAS_BLEND_MODE) != 0;
	}

	inline bool hasCacheAsBitmap()
	{
		return (flags2 & HAS_CACHE_AS_BITMAP) != 0;
	}

	inline bool hasImage()
	{
		return (flags2 & HAS_IMAGE) != 0;
	}

	inline bool hasClassName()
	{
		return (flags2 & HAS_CLASS_NAME) != 0;
	}

	inline bool hasFilterList()
	{
		return (flags2 & HAS_FILTER_LIST) != 0;
	}
public:
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len) override;
};

class TagRemoveObject2
	: public SWFTag
{
public:
	void visit(TagHandler* h) override;
};

class TagRemoveObject
	: public SWFTag
{
public:
	void visit(TagHandler* h) override;
};
#pragma endregion The Display List

#pragma region Images
class TagDefineBits
	: public DefineTag
{
public:
	ByteArray imageData;
};

class TagDefineBitsJPEG2
	: public TagDefineBits
{
public:
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len) override;
};
class TagDefineBitsJPEG3
	: public TagDefineBitsJPEG2
{
public:
	ByteArray alphaData;
public:
	void visit(TagHandler* h) override;
	void setData(char* data, size_t len) override;
};
class TagDefineBitsLossless
	: public DefineTag
{
public:
	UI8 BitmapFormat;
	UI16 BitmapWidth;
	UI16 BitmapHeight;
	ByteArray ZlibBitmapData;
};
class TagDefineBitsLossless2
	: public TagDefineBitsLossless
{

};
#pragma endregion Images

#pragma region Meta data
class TagDefineBinaryData : public DefineTag
{
public:
	ByteArray binary;
public:
	void visit(TagHandler* h) override;
};
#pragma endregion Meta data

class TagUnknow : public SWFTag
{
public:
	TagUnknow(UI16 code);
	void visit(TagHandler* h) override;
	std::string getTagName() const override;
};
#endif // SWFTag_h__
